package com.xbongbong.saas.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.xbongbong.paas.constant.MessageConstant;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.domain.entity.PaasFormExplainEntity;
import com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt;
import com.xbongbong.paas.elasticsearch.model.PaasEsModel;
import com.xbongbong.paas.elasticsearch.pojo.dto.DeleteDTO;
import com.xbongbong.paas.elasticsearch.util.EsHelper;
import com.xbongbong.paas.elasticsearch.util.XbbElasticsearchRestTemplate;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.FieldTypeEnum;
import com.xbongbong.paas.enums.FlowStatusEnum;
import com.xbongbong.paas.enums.IndexTypeEnum;
import com.xbongbong.paas.field.FieldAttrEntity;
import com.xbongbong.paas.help.ProGetHelp;
import com.xbongbong.paas.model.PaasFormExplainModel;
import com.xbongbong.paas.pojo.CreditCardBillInfoPojo;
import com.xbongbong.paas.pojo.dto.CreditCardBillDTO;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.DateTimeUtil;
import com.xbongbong.paas.util.ExplainUtil;
import com.xbongbong.pro.constant.ElasticConstant;
import com.xbongbong.pro.domain.entity.UpdateDataEntity;
import com.xbongbong.pro.enums.SaasMarkEnum;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.pro.service.toolbox.statistic.help.ChartQueryHelp;
import com.xbongbong.pro.service.toolbox.statistic.help.FormHelp;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.ParameterConstant;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.domain.entity.ext.CreditCardBillEntityExt;
import com.xbongbong.saas.domain.entity.ext.CreditLimitEntityExt;
import com.xbongbong.saas.enums.CreditCardBillTypeEnum;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.business.CreditCardBillEnum;
import com.xbongbong.saas.enums.business.CreditLimitEnum;
import com.xbongbong.saas.enums.business.OrderEnum;
import com.xbongbong.saas.enums.business.PaymentDistributorEnum;
import com.xbongbong.saas.help.SaasCreditHelper;
import com.xbongbong.saas.help.SaasSaveHelp;
import com.xbongbong.saas.model.CreditCardBillModel;
import com.xbongbong.saas.service.CreditCardBillService;
import org.apache.commons.collections4.CollectionUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.metrics.SumAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
import static org.elasticsearch.index.query.QueryBuilders.termsQuery;

/**
 * @author xingxing.xiao
 * @date 2020/12/25 11:14
 */
@Service("creditCardBillService")
public class CreditCardBillServiceImpl implements CreditCardBillService {

    private static final Logger LOG = LoggerFactory.getLogger(CreditCardBillServiceImpl.class);

    @Resource
    private CreditCardBillModel creditCardBillModel;
    @Resource
    private PaasFormExplainModel paasFormExplainModel;
    @Resource
    private SaasSaveHelp saasSaveHelp;
    @Resource
    private ChartQueryHelp chartQueryHelp;
    @Resource
    private FormHelp formHelp;
    @Resource
    private PaasEsModel paasEsModel;
    @Resource
    private EsHelper esHelper;
    @Resource
    private XbbElasticsearchRestTemplate xbbElasticsearchRestTemplate;
    @Resource
    private ProGetHelp proGetHelp;
    @Resource
    private SaasCreditHelper saasCreditHelper;

    @Override
    public void addBatchCreditCardBill(CreditCardBillDTO creditCardBillDTO) throws XbbException {
        List<CreditCardBillInfoPojo> creditCardBillInfoList = creditCardBillDTO.getCreditCardBillInfoList();
        if (CollectionUtils.isEmpty(creditCardBillInfoList)) {
            return ;
        }
        String corpid = creditCardBillDTO.getCorpid();
        Integer distributorMark = creditCardBillDTO.getDistributorMark();
        Set<Long> customerIdIn = creditCardBillInfoList.stream().map(c -> c.getCustomerId()).collect(Collectors.toSet());
        int businessType = XbbRefTypeEnum.CREDIT_CARD_BILL.getCode();
        // 获取serialNo
        PaasFormExplainEntity paasFormExplainEntity = paasFormExplainModel.getByBusinessType4Distributor(businessType, corpid, distributorMark);
        if ( Objects.isNull(paasFormExplainEntity) ) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FORM_EXPLAIN_NOT_EXIST);
        }
        Long formId = paasFormExplainEntity.getFormId();
        Map<String, FieldAttrEntity> explainMap = ExplainUtil.getExplainMapByAttr(paasFormExplainEntity.getExplains(), CreditCardBillEnum.CREDIT_CARD_NO.getAttr());
        FieldAttrEntity fieldAttrEntity = explainMap.get(CreditCardBillEnum.CREDIT_CARD_NO.getAttr());
        String serialNo = "";
        // 查询经销商信用
        List<CreditLimitEntityExt> creditLimitEntityExtList = saasCreditHelper.getCreditByCustomerIdIn(customerIdIn, corpid);
        if (CollectionUtils.isEmpty(creditLimitEntityExtList)) {
            //无经销商信用，则直接返回，不用释放
            return;
        }
        //经销商id - 该经销商信用
        Map<Long, CreditLimitEntityExt> distributorMap = creditLimitEntityExtList.stream().collect(Collectors.toMap(d -> d.getData().getLong(CreditLimitEnum.CUSTOMER_ID.getAttr()), Function.identity()));
        List<CreditCardBillEntityExt> insertList = new ArrayList<>();
        for (CreditCardBillInfoPojo infoPojo : creditCardBillInfoList) {
            Long customerId = infoPojo.getCustomerId();
            CreditLimitEntityExt creditLimitEntityExt = distributorMap.get(customerId);
            if (Objects.isNull(creditLimitEntityExt)) {
                //无该经销商的信用，则跳过
                continue;
            }
            serialNo = saasSaveHelp.getSerialNo(serialNo, corpid, formId, fieldAttrEntity);
            CreditCardBillEntityExt entityExt = new CreditCardBillEntityExt();
            entityExt.setCorpid(creditCardBillDTO.getCorpid());
            entityExt.setSerialNo(serialNo);
            entityExt.setFormId(formId);
            entityExt.setAppId(paasFormExplainEntity.getAppId());
            entityExt.setMenuId(paasFormExplainEntity.getMenuId());
            entityExt.setCreatorId(creditCardBillDTO.getUserId());
            entityExt.setAddTime(DateTimeUtil.getInt());
            entityExt.setUpdateTime(DateTimeUtil.getInt());
            infoPojo.setCreatorName(creditCardBillDTO.getLoginUserName());
            entityToData(infoPojo, entityExt, creditLimitEntityExt.getId());
            insertList.add(entityExt);
        }
        if (!insertList.isEmpty()) {
            creditCardBillModel.insertBatch(insertList, false);
        }
    }

    @Override
    public void addRollback(String corpid, CreditCardBillTypeEnum creditCardBillTypeEnum, Long refId) throws XbbException {
        if (Objects.isNull(creditCardBillTypeEnum) || Objects.isNull(refId) || Objects.equals(refId, BasicConstant.ZERO_LONG)) {
            return;
        }
        BoolQueryBuilder boolQueryBuilder = chartQueryHelp.boolQueryBuilder4Base(corpid);
        boolQueryBuilder.filter(termQuery(CreditCardBillEnum.getEsAttr4Keyword(CreditCardBillEnum.TYPE), creditCardBillTypeEnum));
        boolQueryBuilder.filter(termQuery(CreditCardBillEnum.getEsAttr4Keyword(CreditCardBillEnum.BUSINESS_TYPE), creditCardBillTypeEnum.getXbbRefTypeEnum().getCode()));
        IndexTypeEnum indexTypeEnum = IndexTypeEnum.IDX_SAAS_CREDIT_CARD_BILL;
        List<Long> refIdIn = Arrays.asList(refId);
        List<CreditCardBillEntityExt> result = formHelp.getFormListByIds4All(indexTypeEnum, boolQueryBuilder, CreditCardBillEnum.getEsAttr4Keyword(CreditCardBillEnum.REF_ID), refIdIn, Collections.singletonList(BasicConstant.ID), CreditCardBillEntityExt.class);
        if (CollectionUtils.isNotEmpty(result)) {
            //新建失败，则直接删除es
            List<DeleteDTO> deleteUserList = new ArrayList<>();
            for (CreditCardBillEntityExt entity : result) {
                DeleteDTO deleteDTO = new DeleteDTO();
                deleteDTO.setEsId(corpid + "_" + entity.getId());
                deleteDTO.setIndexTypeEnum(indexTypeEnum);
                deleteUserList.add(deleteDTO);
            }
            paasEsModel.physicalDeleteBatchChild(deleteUserList, corpid, indexTypeEnum);
        }
    }

    /**
     *
     * @param orderIds 订货单ids
     * @param corpid
     * @return
     * @throws XbbException
     */
    @Override
    public boolean checkOrderCardBill(List<Long> orderIds, String corpid) throws XbbException {
        List<Long> creditOrderIds = saasCreditHelper.filterCreditOrderIds(orderIds, corpid);
        return creditOrderIds.size() > 0;
    }

    /**
     * 计算信用需要还的额度
     * @param paymentIds
     * @param corpid
     * @return
     * @throws XbbException
     */
    @Override
    public BigDecimal calcCreditPayLimit(Long distributorId, List<Long> orderIds, List<Long> paymentIds, String corpid) throws XbbException {
        // 查询订货单流水
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery(ParameterConstant.CORPID + StringConstant.POINT + StringConstant.KEY_WORD, corpid));
        boolQueryBuilder.filter(termQuery(ParameterConstant.DEL, DelEnum.NORMAL.getDel()));
        boolQueryBuilder.filter(termsQuery(CreditCardBillEnum.getEsAttr4Keyword(CreditCardBillEnum.REF_ID), orderIds));
        boolQueryBuilder.filter(termQuery(CreditCardBillEnum.getEsAttr4Keyword(CreditCardBillEnum.TYPE), CreditCardBillTypeEnum.ORDER.getCode()));
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(boolQueryBuilder);
        sourceBuilder.fetchSource(new String[]{"id", "data." + CreditCardBillEnum.REF_ID.getAttr()}, null);
        SearchRequest searchRequest = new SearchRequest(IndexTypeEnum.IDX_SAAS_CREDIT_CARD_BILL.getIndex());
        searchRequest.source(sourceBuilder);
        List<PaasFormDataEntityExt> dataEntityList = paasEsModel.list(sourceBuilder, IndexTypeEnum.IDX_SAAS_CREDIT_CARD_BILL, Collections.singletonList(StringConstant.JSON_DATA), 1, orderIds.size());
        List<Long> creditOrderIds = new ArrayList<>();
        dataEntityList.forEach(item -> creditOrderIds.add(item.getData().getLong(CreditCardBillEnum.REF_ID.getAttr())));

        // 查询跟信用相关的待付款
        boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery(ParameterConstant.CORPID + StringConstant.POINT + StringConstant.KEY_WORD, corpid));
        boolQueryBuilder.filter(termsQuery("id", paymentIds));
        boolQueryBuilder.filter(termQuery(PaymentDistributorEnum.getEsAttr4Keyword(PaymentDistributorEnum.DISTRIBUTOR), distributorId));
        boolQueryBuilder.filter(termsQuery(PaymentDistributorEnum.getEsAttr4Keyword(PaymentDistributorEnum.ORDER), creditOrderIds));
        boolQueryBuilder.filter(termQuery(ParameterConstant.DEL, DelEnum.NORMAL.getDel()));
        boolQueryBuilder.filter(termsQuery("flowStatus", Arrays.asList(FlowStatusEnum.NOT_NEED_APPROVAL.getType(), FlowStatusEnum.PASS.getType())));
        boolQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD, IndexTypeEnum.IDX_SAAS_PAYMENT.getType()));
        SumAggregationBuilder sumAggregationBuilder = AggregationBuilders.sum("agg001").field(PaymentDistributorEnum.getEsAttr4Keyword(PaymentDistributorEnum.AMOUNT));
        SearchRequest paySearchRequest = new SearchRequest(IndexTypeEnum.IDX_SAAS_PAYMENT.getIndex());
        SearchSourceBuilder paySourceBuilder = new SearchSourceBuilder();
        paySourceBuilder.query(boolQueryBuilder);
        // paySourceBuilder.fetchSource(new String[]{"id", "data." + PaymentDistributorEnum.ORDER.getAttr()}, null);
        paySourceBuilder.aggregation(sumAggregationBuilder);
        paySourceBuilder.size(BasicConstant.ZERO);
        paySearchRequest.source(paySourceBuilder);
        JSONObject responseJson = xbbElasticsearchRestTemplate.getAggResponseJson(paySearchRequest);
        BigDecimal result = responseJson.getJSONObject("aggregations").getJSONObject("agg001").getBigDecimal("value");
        return result;
    }

    /**
     * 计算需要释放的剩余额度
     * @param distributorId
     * @param orderId
     * @param corpid
     * @return
     * @throws XbbException
     */
    @Override
    public BigDecimal calcCreditRemainLimit(Long distributorId, Long orderId, String corpid) throws XbbException {
        BigDecimal result = new BigDecimal(0);
        // 订货单
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery(CreditCardBillEnum.getEsAttr4Keyword(CreditCardBillEnum.TYPE), CreditCardBillTypeEnum.ORDER.getCode()));
        boolQueryBuilder.filter(termQuery(CreditCardBillEnum.getEsAttr4Keyword(CreditCardBillEnum.REF_ID), orderId));
        BigDecimal orderLimit = getOrderCreditAggSum(corpid, boolQueryBuilder, IndexTypeEnum.IDX_SAAS_CREDIT_CARD_BILL);

        if (Objects.nonNull(orderLimit)) {
            // 查询该订货单的回款金额（即：已收金额）
            BigDecimal finishAmount = new BigDecimal(0);
            PaasFormDataEntityExt paasFormDataEntityExt = proGetHelp.getByKey(orderId, XbbRefTypeEnum.CONTRACT.getCode(), SaasMarkEnum.SAAS.getCode(), corpid);
            if (Objects.nonNull(paasFormDataEntityExt)) {
                JSONObject data = paasFormDataEntityExt.getData();
                finishAmount = data.getBigDecimal(OrderEnum.FINISH_AMOUNT.getAttr());
            }
            result = orderLimit.subtract(finishAmount);
        }
        return result;
    }

    /**
     * 查询订货单相关流水
     * @param corpid
     * @param boolQueryBuilder
     * @return
     * @throws XbbException
     */
    private BigDecimal getOrderCreditAggSum(String corpid, BoolQueryBuilder boolQueryBuilder, IndexTypeEnum indexTypeEnum) throws XbbException {

        // 共用查询参数
        boolQueryBuilder.filter(termQuery(ParameterConstant.CORPID + StringConstant.POINT + StringConstant.KEY_WORD, corpid));
        boolQueryBuilder.filter(termQuery(ParameterConstant.DEL, DelEnum.NORMAL.getDel()));
        boolQueryBuilder.filter(termsQuery("flowStatus", Arrays.asList(FlowStatusEnum.NOT_NEED_APPROVAL.getType(), FlowStatusEnum.PASS.getType())));
        boolQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD, indexTypeEnum.getType()));

        SumAggregationBuilder sumAggregationBuilder = AggregationBuilders.sum("agg001").field(CreditCardBillEnum.getEsAttr4Keyword(CreditCardBillEnum.AMOUNT));
        SearchRequest searchRequest = new SearchRequest(indexTypeEnum.getIndex());
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(boolQueryBuilder);
        sourceBuilder.aggregation(sumAggregationBuilder);
        sourceBuilder.size(BasicConstant.ZERO);
        searchRequest.source(sourceBuilder);
        JSONObject responseJson = xbbElasticsearchRestTemplate.getAggResponseJson(searchRequest);
        BigDecimal result = responseJson.getJSONObject("aggregations").getJSONObject("agg001").getBigDecimal("value");
        return result;
    }

    /**
     * 名称一致性同步
     * @param corpid
     * @param customerId
     * @param customerName
     * @return
     * @author xingxing.xiao
     * @date 2021/02/25 16:48
     */
    @Override
    public void dataConsistencyUpdateCustomer(String corpid, Long customerId, String customerName) throws XbbException {
        try {
            BoolQueryBuilder filter = boolQuery()
                    .filter(termQuery("corpid.keyword", corpid))
                    .filter(termQuery("del", 0))
                    .filter(termQuery(CreditCardBillEnum.getEsAttr4Keyword(CreditCardBillEnum.CUSTOMER_ID), customerId));
            List<String> fieldList = new ArrayList<>();
            fieldList.add(FieldTypeEnum.DATAID.getAlias());
            List<PaasFormDataEntityExt> esEntities = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_CREDIT_CARD_BILL, filter, PaasFormDataEntityExt.class, fieldList);
            Set<Long> ids = new HashSet<>();
            for (PaasFormDataEntityExt esEntity : esEntities) {
                ids.add(esEntity.getDataId());
            }
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("corpid", corpid);
            param.put("idIn", ids);
            param.put("del", 0);
            List<CreditCardBillEntityExt> entityList = creditCardBillModel.findEntities(param);
            if (CollectionUtils.isNotEmpty(entityList)) {
                List<UpdateDataEntity> updateList = new ArrayList<>();
                for (CreditCardBillEntityExt entity : entityList) {
                    JSONObject jsonObject = new JSONObject();
                    jsonObject.put(CreditCardBillEnum.CUSTOMER_NAME.getAttr(), customerName);
                    UpdateDataEntity updateData = ExplainUtil.getUpdateData(entity.getId(), jsonObject, corpid);
                    updateList.add(updateData);
                }
                creditCardBillModel.updateBatch(updateList, corpid);
            }
        } catch (Exception e) {
            LOG.error("CreditFixedLimitServiceImpl.dataConsistencyUpdateCustomer 出错， corpid=" + corpid + " customerId=" + customerId + " customerName=" + customerName, e);
        }
    }

    /**
     * 格式转换
     * @param creditCardBillInfoPojo
     * @return
     */
    private void entityToData(CreditCardBillInfoPojo creditCardBillInfoPojo, CreditCardBillEntityExt creditCardBillEntityExt, Long creditId) {
        JSONObject data = new JSONObject();
        data.put(CreditCardBillEnum.TYPE.getAttr(), creditCardBillInfoPojo.getType());
        data.put(CreditCardBillEnum.BUSINESS_TYPE.getAttr(), creditCardBillInfoPojo.getBusinessType());
        data.put(CreditCardBillEnum.REF_ID.getAttr(), creditCardBillInfoPojo.getRefId());
        data.put(CreditCardBillEnum.REF_NO.getAttr(), creditCardBillInfoPojo.getRefNo());
        data.put(CreditCardBillEnum.AMOUNT.getAttr(), creditCardBillInfoPojo.getAmount());
        data.put(CreditCardBillEnum.CUSTOMER_ID.getAttr(), creditCardBillInfoPojo.getCustomerId());
        data.put(CreditCardBillEnum.CUSTOMER_NAME.getAttr(), creditCardBillInfoPojo.getCustomerName());
        data.put(CreditCardBillEnum.CREDIT_ID.getAttr(), creditId);
        data.put(CreditCardBillEnum.CREATOR_ID.getAttr(), creditCardBillEntityExt.getCreatorId());
        data.put(CreditCardBillEnum.CREATOR_NAME.getAttr(), creditCardBillInfoPojo.getCreatorName());
        data.put(CreditCardBillEnum.CREATOR_IS_DISTRIBUTOR.getAttr(), creditCardBillInfoPojo.getCreatorIsDistributor());
        creditCardBillEntityExt.setData(data);
    }
}
